﻿using FFmpeg.AutoGen;
using System;

namespace FFmpegLib
{
    public sealed unsafe class FSwrContext : IDisposable
    {
        private SwrContext* swrCtx;
        public SwrContext* Ctx { get => swrCtx; }

        public bool Inited { get; private set; }

        public FSwrContext(FAVFrame inFrame, AVSampleFormat out_fmt = AVSampleFormat.AV_SAMPLE_FMT_S16)
        {
            Inited = Init(inFrame, out_fmt);
        }

        private bool Init(FAVFrame inFrame, AVSampleFormat out_fmt = AVSampleFormat.AV_SAMPLE_FMT_S16)
        {
            SwrContext* ctx = ffmpeg.swr_alloc();
            AVSampleFormat in_fmt = (AVSampleFormat)(inFrame.Format);
            int in_sample_rate = inFrame.SampleRate;
            long in_channel_layout = (long)(inFrame.ChannelLayout);

            int out_sample_rate = in_sample_rate;
            long out_channel_layout = in_channel_layout;

            ctx = ffmpeg.swr_alloc_set_opts(ctx,
                                              in_channel_layout,//ffmpeg.av_get_default_channel_layout(output_codec_context->channels),
                                              out_fmt,
                                              out_sample_rate,
                                              out_channel_layout,//av_get_default_channel_layout(input_codec_context->channels),
                                              in_fmt,//input_codec_context->sample_fmt,
                                              in_sample_rate,//input_codec_context->sample_rate,
                                              0, null);
            int ret = ffmpeg.swr_init(ctx);
            if (ret < 0)
            {
                goto Error;
            }
            swrCtx = ctx;
            return Inited = true;

        Error:
            ffmpeg.swr_free(&ctx);
            ctx = null;
            return false;
        }

        public PcmFrame ConvertAudioPcmData(FAVFrame inFrame, AVSampleFormat dstFmt)
        {
            PcmFrame pcm = new PcmFrame(inFrame.Channels, inFrame.NbSamples, dstFmt);
            pcm.SampleRate = inFrame.SampleRate;
            AVSampleFormat cur_fmt = (AVSampleFormat)inFrame.Format;
            int is_planar = ffmpeg.av_sample_fmt_is_planar(cur_fmt);

            if (dstFmt == cur_fmt && is_planar == 0)
            {
                System.Runtime.CompilerServices.Unsafe.CopyBlock(pcm.Pcm, inFrame.Ptr->data[0], (uint)pcm.Size);
                return pcm;
            }

            byte* prt = (byte*)pcm.Pcm;
            pcm.NbSamples = ffmpeg.swr_convert(swrCtx, &prt, inFrame.NbSamples, inFrame.Ptr->extended_data, inFrame.NbSamples);
            return pcm;
        }

        #region dispose
        private bool disposedValue;
        protected void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    if (Ctx != null)
                    {
                        ffmpeg.swr_close(Ctx);
                        fixed (SwrContext** ptr = &swrCtx)
                            ffmpeg.swr_free(ptr);
                        swrCtx = null;
                    }
                }
                disposedValue = true;
            }
        }

        public void Dispose()
        {
            // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }
        #endregion
    }
}